#pragma once

#include <iostream>
#include <algorithm>
#include <functional>
#include <string>
#include <jsoncpp/json/json.h>

#include "../common/httplib.h"
#include "oj_model.hpp"
#include "oj_view.hpp"
#include "./load_balance.hpp"

namespace yx
{
    class Controller
    {
    private:
        Model _model;
        View _view;
        LoadBalancer _load_balancer;

    public:
        Controller() {}
        ~Controller() {}

        bool allQuestions(std::string &html)
        {
            bool ret = true;
            std::vector<struct Question> all;

            if (_model.getAllQuestions(all))
            {
                // 获取题目信息成功，将所有的题目数据构建成网页
                std::sort(all.begin(), all.end(), std::less<struct Question>());
                _view.allExpandHtml(all, html);
            }
            else
            {
                html = "获取题目失败, 形成题目列表失败";
                ret = false;
            }

            return ret;
        }

        bool oneQuestion(const std::string &number, std::string &html)
        {
            bool ret = true;

            Question q;

            if (_model.getOneQuestion(number, q))
            {
                _view.oneExpandHtml(q, html);
            }
            else
            {
                html = "指定题目: " + number + " 不存在!";
                ret = false;
            }

            return ret;
        }

        // jsin:
        //     "code":
        //     "input":
        bool judge(const std::string &number, const std::string &in_json, std::string &out_json)
        {
            // 0.获取对应题目内容
            struct Question q;
            if (!_model.getOneQuestion(number, q))
            {
                return false;
            }
            // 1.反序列jsin得到用户提交的代码及题目编号
            Json::Value in_val;
            Json::Reader reader;
            reader.parse(in_json, in_val);
            // 2.将用户提交的代码与测试用例拼接成一个完整的代码
            Json::Value cr_val;
            Json::FastWriter writer;
            cr_val["code"] = in_val["code"].asString() + q._tail;
            cr_val["input"] = in_val["input"];
            cr_val["cpu_limit"] = q._cpulimit;
            cr_val["mem_limit"] = q._memlimit;
            std::string cr_json = writer.write(cr_val);
            // 3.负载均衡选择一个编译运行主机向其发送请求
            while (true)
            {
                Machine m;
                int id;

                // 选择负载最小的主机
                if (!_load_balancer.getMinLoadMachine(id, m))
                {
                    return false;
                }

                httplib::Client cli(m.getIp(), m.getPort());
                // 增加负载
                ++m;
                ((ILOG()("选择主机({})成功负载= {}, [{}:{}]!!!", id, (int)m.getLoad(), m.getIp(), m.getPort())));
                auto res = cli.Post("/compile_run", cr_json, "application/json;charset=utf-8");
                // 减少负载
                --m;

                if (res)
                {
                    // 请求编译运行成功
                    if (res->status == 200)
                    {
                        // 4.将得到结果进行返回
                        out_json = res->body;

                        (ILOG()("请求编译和运行服务成功!!!"));
                        break;
                    }
                }
                else
                {
                    _load_balancer.offlineMachine(id);
                    _load_balancer.showMachines();
                    (ELOG()("主机id= {}, [{}:{}] 可能已经离线!!!", id, m.getIp(), m.getPort()));
                }
            }
            return true;
        }

        void recovery()
        {
            _load_balancer.onlineMachines();
        }
    };

}